
#include "cpu.h"

struct stats_cpu *st_working_cpu;
struct stats_cpu *st_record_cpu;
unsigned long cpu_working_time, cpu_record_time;
float cpu_usage = 0;
unsigned long long cpu_working_uptime, cpu_record_uptime;
unsigned long long total_delta_time;
unsigned long long total_cpu_idle;
int cpu_initialized = 0;
unsigned long long interval;
int is_cpu_recording = 0;
FILE *cpu_logger;
char cpu_filename[BUFFERSIZE];

int cpu_nbr = 0;     /* Nb of processors on the machine */

static int r_is_cpu_recording = 0;

void set_cpu_logging()
{
    if (loggable && !r_is_cpu_recording && is_cpu_recording) {
        r_is_cpu_recording = 1;
        char timesuffix[BUFFERSIZE];
        make_filename(timesuffix);
        snprintf(cpu_filename, BUFFERSIZE, "%s/cpu_%s.log", LOGGER_PATH, timesuffix);
        cpu_logger = fopen(cpu_filename, "w");
        if (cpu_logger == NULL) {
            loggable = 0;
            // TODO notify UI that unable to create a file
            __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "cpu logging failed\n");
            return;
        }
        fprintf(cpu_logger, "seconds,load%%,user,nice,system,idle,iowait,hardirq,softirq,steal,guest\n");
        __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "cpu logging started\n");
    } else if (r_is_cpu_recording && !is_cpu_recording) {
        r_is_cpu_recording = 0;
        if (cpu_logger == NULL) return;
        fclose(cpu_logger);
        __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "cpu logging stoped\n");
        struct stat st;
        stat(cpu_filename, &st);
        if (st.st_size == 0) {
            __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "empty cpu logging removed\n");
            remove(cpu_filename);
        }
    }
}

void cpu_cleanup()
{
    if (st_working_cpu) free(st_working_cpu);
    if (st_record_cpu) free(st_record_cpu);
    if (cpu_logger != NULL) fclose(cpu_logger);
}

void init_cpu()
{
    int i;
    st_working_cpu = (struct stats_cpu*)malloc(sizeof(struct stats_cpu));
    st_record_cpu = (struct stats_cpu*)malloc(sizeof(struct stats_cpu));
    memset(st_working_cpu, 0, sizeof(struct stats_cpu));
    memset(st_record_cpu, 0, sizeof(struct stats_cpu));
    cpu_working_time = cpu_record_time = 0;
    cpu_working_uptime = cpu_record_uptime = 0;
    // Read CPU status into st_working_cpu
    cpu_dump();

    is_cpu_recording = 0;
    // Mark that CPU has been initialized.
    cpu_initialized = 1;
}

void cpu_dump()
{
    FILE *fp;
    fp = fopen(STAT, "r");
    if (fp == NULL) return;
    fscanf(fp, "cpu  %llu %llu %llu %llu %llu %llu %llu %llu %llu",
               &st_working_cpu->cpu_user,
               &st_working_cpu->cpu_nice,
               &st_working_cpu->cpu_sys,
               &st_working_cpu->cpu_idle,
               &st_working_cpu->cpu_iowait,
               &st_working_cpu->cpu_hardirq,
               &st_working_cpu->cpu_softirq,
               &st_working_cpu->cpu_steal,
               &st_working_cpu->cpu_guest);
    fclose(fp);

    cpu_working_uptime = st_working_cpu->cpu_user + st_working_cpu->cpu_nice +
            st_working_cpu->cpu_sys  + st_working_cpu->cpu_idle +
            st_working_cpu->cpu_iowait + st_working_cpu->cpu_hardirq +
            st_working_cpu->cpu_steal  + st_working_cpu->cpu_softirq;
    cpu_working_time = cpu_working_uptime - st_working_cpu->cpu_steal;
    total_delta_time = cpu_working_time - cpu_record_time;
    total_cpu_idle = st_working_cpu->cpu_idle - st_record_cpu->cpu_idle + (st_working_cpu->cpu_iowait - st_record_cpu->cpu_iowait);
    interval = get_interval(cpu_record_uptime, cpu_working_uptime);
    //__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "working idle %llu\n", st_working_cpu->cpu_idle);
    //__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "record idle %llu\n", st_record_cpu->cpu_idle);
    //__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "total_cpu_idle %llu\n", total_cpu_idle);
    swap_ptr((void*)&st_working_cpu, (void*)&st_record_cpu);
    swap_long(&cpu_working_time, &cpu_record_time);
    swap_ll(&cpu_working_uptime, &cpu_record_uptime);
}

void refresh_cpu_usage()
{
    cpu_usage = (100 - (total_cpu_idle * 100.f / total_delta_time));
    if (is_cpu_recording && loggable) {
                            //sec usage user nice sys  idle iow  hirq sirq steal guest
        fprintf(cpu_logger, "%llu,%0.3f,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", 
                            system_time, 
                            cpu_usage,
                            st_record_cpu->cpu_user,
                            st_record_cpu->cpu_nice,
                            st_record_cpu->cpu_sys,
                            st_record_cpu->cpu_idle,
                            st_record_cpu->cpu_iowait,
                            st_record_cpu->cpu_hardirq,
                            st_record_cpu->cpu_softirq,
                            st_record_cpu->cpu_steal,
                            st_record_cpu->cpu_guest);
        fflush(cpu_logger);
        //__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "logging %llu %d\n", (unsigned long long)time(NULL), cpu_usage);
    }
}

long get_cpu_deltatime()
{
    return total_delta_time;
}

long get_cpu_idle()
{
    return total_cpu_idle;
}

int get_cpu_usage()
{
    return (int)cpu_usage;
}
